import javax.media.j3d.*;
import javax.vecmath.*;

public class Hand extends TransformGroup 
{

	/* finger groups */
	private TransformGroup rightFinger = new TransformGroup();
	private TransformGroup leftFinger = new TransformGroup();
	private TransformGroup topLeftFinger = new TransformGroup();
	private TransformGroup thumb = new TransformGroup();
	private TransformGroup topRightFinger = new TransformGroup();
	private TransformGroup [] fingers = {rightFinger, leftFinger, topLeftFinger, thumb, topRightFinger};
	
	/* finger group positions - each finger has either an on or off position */
	private Vector3d [][] fingerPositions = 
		{
			{new Vector3d(1.4, 0, 0), new Vector3d(0.9, 0, 0)},                 /* right finger on and off state */
			{new Vector3d(-1.4, 0, 0), new Vector3d(-0.9, 0, 0)},               /* left finger on and off state */
			{new Vector3d (-0.45, 1.1, 0.68), new Vector3d (-0.45, 0.6, 0.68)}, /* top-left finger on and off state */
			{new Vector3d(0.0, -1.1, 0.68), new Vector3d(0.0, -0.6, 0.68)},     /* thumb on and off state */
			{new Vector3d(0.45, 1.1, 0.68), new Vector3d(0.45, 0.6, 0.68)}};    /* top-right finger on and off state */

	/** button states for this hand. They are, in order, left, right, top-left,
	 * top-middle and top-right */
	private boolean [] buttonStates = {false, false, false, false, false};
	
	public static final int LEFT_BUTTON = 0;
	public static final int RIGHT_BUTTON = 1;
	public static final int TOP_LEFT_BUTTON = 2;
	public static final int TOP_MIDDLE_BUTTON = 3;
	public static final int TOP_RIGHT_BUTTON = 4;
	
	/** specifies an offset from the sensor to the actual position of the user's hand */
	private static final Vector3f offset = new Vector3f(0,0.5f,0);
	
	
	public Hand() 
	{
		super();
		this.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		this.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		TransformGroup scale = new TransformGroup();
		
		Shape3D main = new Shape3D();
		BoxGeometry main_geometry = new BoxGeometry();
		
		//main_geometry.squash_front(0.1);
		main.setGeometry(main_geometry);
		main.setAppearance(myAppearance2());
		scale.addChild(main);
		
		generateHandPiece(rightFinger, 0.4f);
		scale.addChild(rightFinger);
		generateHandPiece(leftFinger, 0.4f);
		scale.addChild(leftFinger);
		generateHandPiece(topLeftFinger, 0.3f);
		scale.addChild(topLeftFinger);
		generateHandPiece(thumb, 0.3f);
		scale.addChild(thumb);
		generateHandPiece(topRightFinger, 0.3f);
		scale.addChild(topRightFinger);
		
		for (int i = 0; i < 5; i++)
			updateButtonVisualRepresentation(i);
		
		Transform3D scale_transform = new Transform3D();
		scale_transform.setScale(0.075);
		scale_transform.setTranslation(offset);
		scale.setTransform(scale_transform);
		this.addChild(scale);
	}
	
	private void generateHandPiece(TransformGroup pieceGroup, float scale)
	{
		Shape3D pieceShape = new Shape3D();
		pieceGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		pieceGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		Transform3D transform = new Transform3D();
		transform.setScale(scale);
		pieceGroup.setTransform(transform);
		pieceShape.setGeometry(new BoxGeometry());
		pieceShape.setAppearance(getAppearance());
		pieceGroup.addChild(pieceShape);
	}
	
	private Appearance getAppearance() 
	{
		Appearance app = new Appearance();
		Color3f ambnCol = new Color3f(0.3f, 0.3f, 0.3f);
		Color3f emmsCol = new Color3f(0f, 0f, 0f);
		Color3f diffCol = new Color3f(0.1f, 0.5f, 0.2f);
		Color3f specCol = new Color3f(0.0f, 0.0f, 0.0f);
		float shininess = 80.0f;
		Material mat = new Material(ambnCol, emmsCol, diffCol, specCol, shininess);
		mat.setLightingEnable(true);
		app.setMaterial(mat);
		
		ColoringAttributes ca = app.getColoringAttributes();
		if (ca == null)
			ca = new ColoringAttributes();
		ca.setShadeModel(ColoringAttributes.SHADE_GOURAUD);
		app.setColoringAttributes(ca);
		PolygonAttributes pa = app.getPolygonAttributes ();
		
		if (pa == null)
			pa = new PolygonAttributes ();
		
		pa.setCullFace(PolygonAttributes.CULL_BACK);
		app.setPolygonAttributes(pa);
		return app;
	}
	
	private Appearance myAppearance2 () 
	{
		Appearance app = getAppearance();
		Color3f ambnCol = new Color3f (0.0f, 1.0f, 1.0f);
		Color3f emmiCol = new Color3f (0f, 0f, 0f);
		Color3f diffCol = new Color3f (0.1f, 0.5f, 0.2f);
		Color3f specCol = new Color3f (0.0f, 0.0f, 0.0f);
		float shininess = 80.0f;
		Material mat = new Material(ambnCol, emmiCol, diffCol, specCol, shininess);
		mat.setLightingEnable(true);
		app.setMaterial(mat);
		return app;
	}   
	
	public void setLeftButtonState(boolean state){
		setButtonState(Hand.LEFT_BUTTON, state);
	}
	
	public boolean getLeftButtonState(){
		return getButtonState(Hand.LEFT_BUTTON);
	}
	
	public void setRightButtonState(boolean state){
		setButtonState(Hand.RIGHT_BUTTON, state);
	}
	
	public boolean getRightButtonState(){
		return getButtonState(Hand.RIGHT_BUTTON);
	}
	
	public void setTopLeftButtonState(boolean state){
		setButtonState(Hand.TOP_LEFT_BUTTON, state);
	}
	
	public void setTopMiddleButtonState(boolean state){
		setButtonState(Hand.TOP_MIDDLE_BUTTON, state);
	}
	
	public void setTopRightButtonState(boolean state){
		setButtonState(Hand.TOP_RIGHT_BUTTON, state);
	}
	
	public void setButtonState(int button, boolean state){
		buttonStates[button] = state;
		updateButtonVisualRepresentation(button);
	}
	
	public boolean getButtonState(int button){
		return buttonStates[button];
	}
	
	public void toggleButton(int button) {
		buttonStates[button] = !buttonStates[button];
		updateButtonVisualRepresentation(button);
	}
	
	/** updates a button's visual representation on screen to reflect the
	 * state of the actual button. A button is represented on screen as a
	 * finger.
	 * 
	 * @param button the index of the button to update
	 */
	private void updateButtonVisualRepresentation(int button){
		boolean buttonState = buttonStates[button];
		Transform3D transform = new Transform3D();
		fingers[button].getTransform(transform);
		transform.setTranslation(fingerPositions[button][buttonState ? 1 : 0]);
		fingers[button].setTransform(transform);
		
	}

	Vector3f previousHandPosition = new Vector3f();
	Vector3f currentHandPosition = new Vector3f();
	Vector3f handVelocity = new Vector3f();
	
	public void setTransform(Transform3D transform){
		transform.get(currentHandPosition);
		handVelocity.sub(currentHandPosition, previousHandPosition);
		previousHandPosition.set(currentHandPosition);
		super.setTransform(transform);
	}
	
	public TransformGroup getTransformGroup(){
		return this;
	}
}
